﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Python
{

    class Snake
    {
        int _NumberOfWertebras;
        public bool IsRabbitCauht {get; set;}
        enum Direction {Stand, GoUp, GoDown, GoLeft, GoRight};
        Direction current;
        Direction wantToGo;
        Point WertebraTMP = new Point(Controller._xSnakeStartPosition, Controller._ySnakeStartPosition);
        Point WertebraLast = new Point(Controller._xSnakeStartPosition, Controller._ySnakeStartPosition);
        SortedDictionary<int, Point> body = new SortedDictionary<int, Point>();
        public Point GetHeadSnakePoint    // The property offers a point where the Snake head is located.
        { 
            get 
            { 
                Point HeadSnakePoint = new Point(0, 0);
                HeadSnakePoint.X = body[0].X;
                HeadSnakePoint.Y = body[1].Y;
                return HeadSnakePoint;
            } 
        }

        int _origCol;
        int _origRow;

        public Snake()
        {
            _NumberOfWertebras = 3;
            Point Wertebra;
            current = Direction.Stand;
            wantToGo = Direction.Stand;

            Conf GameConfig = new Conf();
            _origCol = GameConfig._origCol;
            _origRow = GameConfig._origRow;

            for (int i = 0; i < _NumberOfWertebras; i++)
            {
                Wertebra = new Point(Controller._xSnakeStartPosition, Controller._ySnakeStartPosition + i);
                body.Add(i, Wertebra);
            }           
        }

        public Snake(int NumberOfWertebras)
        {
            _NumberOfWertebras = NumberOfWertebras;
            Point Wertebra;
            Point WertebraTMP = new Point(Controller._xSnakeStartPosition, Controller._ySnakeStartPosition);

            current = Direction.Stand;
            wantToGo = Direction.Stand;

            Conf GameConfig = new Conf();
            _origCol = GameConfig._origCol;
            _origRow = GameConfig._origRow;

            for (int i = 0; i < _NumberOfWertebras; i++)
            {
                Wertebra = new Point(Controller._xSnakeStartPosition, Controller._ySnakeStartPosition + i);
                body.Add(i, Wertebra);
            }
        }   
        
        public void WantGoUp()
        {
            wantToGo = Direction.GoUp;
            EraseTailTrack();
            RecalculateBody();
            DrawSnake();
        }
        public void WantToStand()
        {
            wantToGo = Direction.Stand;
            DrawSnake();
        }
        public void WantGoDown()
        {
            wantToGo = Direction.GoDown;
            EraseTailTrack();
            RecalculateBody();
            DrawSnake();
        }
        public void WantGoLeft()
        {
            wantToGo = Direction.GoLeft;
            EraseTailTrack();
            RecalculateBody();
            DrawSnake();
        }
        public void WantGoRight()
        {
            EraseTailTrack();
            wantToGo = Direction.GoRight;
            RecalculateBody();
            DrawSnake();
        }        
        public void RecalculateHeader()
        { // this operation must do after RecalculateBody only! Previous values  of the head is destroyed.
                switch (wantToGo)
                {
                    case Direction.Stand:
                        // without any changing in position.
                        break;
                    case Direction.GoUp:
                        --body[0].Y;
                        break;
                    case Direction.GoDown:
                        ++body[0].Y;
                        break;
                    case Direction.GoLeft:
                        --body[0].X;
                        break;
                    case Direction.GoRight:
                        ++body[0].X;
                        break;
                    default:
                        throw new Exception("Program must not be here in default of \"switch (wantToGo)\"");
                }
            return;
        }
        public void RecalculateBody()
        {
            /*
            if (IsRabbitCauht == true)
            {
                WertebraLast.X = body[_NumberOfWertebras - 1].X;
                WertebraLast.Y = body[_NumberOfWertebras - 1].Y;
                _NumberOfWertebras++;
                body.Add(_NumberOfWertebras - 1, WertebraLast);
            }
            */
            for (int i = _NumberOfWertebras - 1; i > 0; i--)
            {
                body[i].X = body[i - 1].X;
                body[i].Y = body[i - 1].Y;
            }            
            preventAttemptToReverse();               
            RecalculateHeader();            
            current = wantToGo; // To remember a new Snake movement direction 
            // DrawSnake();
            IsRabbitCauht = false;
        }
        public void RecalculateNewBody()
        {
            EraseTailTrack(); 
            for (int i = _NumberOfWertebras - 1; i > 0; i--)
            {
                body[i].X = body[i - 1].X;
                body[i].Y = body[i - 1].Y;
            }
            RecalculateHeader();
            Point tmpPoint = new Point(Controller._xSnakeStartPosition, Controller._ySnakeStartPosition);
            tmpPoint.X = body[_NumberOfWertebras - 1].X;
            tmpPoint.Y = body[_NumberOfWertebras - 1].Y;
            _NumberOfWertebras++;
            body.Add(_NumberOfWertebras - 1, tmpPoint);
            //--
            for (int i = _NumberOfWertebras - 2; i > 0; i--)
            {
                body[i].X = body[i - 1].X;
                body[i].Y = body[i - 1].Y;
            }
            preventAttemptToReverse();
            RecalculateHeader();
            DrawSnake();
            current = wantToGo; // To remember a new Snake movement direction 
            IsRabbitCauht = false;
        }

        public bool IsCut()
        {
            for(int i = 0; i < _NumberOfWertebras; i++)
            {
                if (body[0].X == body[i].X && body[0].Y == body[i].Y)
                {
                    return true;
                }                
            }
            return false;
        }
        public void preventAttemptToReverse()  
        {
            WertebraTMP.X = body[0].X;
            WertebraTMP.Y = body[0].Y;
            switch (wantToGo)
            {
                case Direction.Stand:
                    // without any changing in position.
                    break;
                case Direction.GoUp:
                    --WertebraTMP.Y;
                    break;
                case Direction.GoDown:
                    ++WertebraTMP.Y;
                    break;
                case Direction.GoLeft:
                    --WertebraTMP.X;
                    break;
                case Direction.GoRight:
                    ++WertebraTMP.X;
                    break;
                default:
                    throw new Exception("Program must not be here in default of \"switch (wantToGo)\"");
            }
            if (WertebraTMP.X == body[2].X && WertebraTMP.Y == body[2].Y)
            {
                wantToGo = current;
                Console.Beep();
            }
                                
        }         
        public void CheckIncursionOnBorder()
        {

        }
        public void EraseTailTrack()
        {
            Depict drawBody = new Depict();
            drawBody.WriteAt(" ", body[_NumberOfWertebras-1].X, body[_NumberOfWertebras-1].Y, _origCol, _origRow);
        }
        public void DrawSnake()
        {
            Depict drawBody = new Depict();
            for(int i = 0; i < _NumberOfWertebras; i++)
            {
                if (i == 0)
                {
                    drawBody.WriteAt("O", body[i].X, body[i].Y, _origCol, _origRow);
                }
                else drawBody.WriteAt("#", body[i].X, body[i].Y, _origCol, _origRow);
            }
        }        
        public void ContinueMovementSnake()
        {
            switch (current)
            {
                case Direction.Stand:
                    WantToStand();
                    break;
                case Direction.GoUp:
                    WantGoUp();
                    break;
                case Direction.GoDown:
                    WantGoDown();
                    break;
                case Direction.GoLeft:
                    WantGoLeft();
                    break;
                case Direction.GoRight:
                    WantGoRight();
                    break;
                default:
                    throw new Exception("Program must not be here \"switch (current)\"");
            }
        }   
    }
}
